/*
 * $Id: JXColorSelectionButton.java 4082 2011-11-15 18:39:43Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx;

import static java.awt.RenderingHints.KEY_ANTIALIASING;
import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.jdesktop.swingx.color.EyeDropperColorChooserPanel;
import org.jdesktop.swingx.plaf.UIManagerExt;
import org.jdesktop.swingx.util.GraphicsUtilities;
import org.jdesktop.swingx.util.OS;
import org.jdesktop.swingx.util.PaintUtils;

/**
 * A button which allows the user to select a single color. The button has a
 * platform specific look. Ex: on Mac OS X it will mimic an NSColorWell. When
 * the user clicks the button it will open a color chooser set to the current
 * background color of the button. The new selected color will be stored in the
 * background property and can be retrieved using the getBackground() method. As
 * the user is choosing colors within the color chooser the background property
 * will be updated. By listening to this property developers can make other
 * parts of their programs update.
 *
 * @author joshua@marinacci.org
 */
public class JXColorSelectionButton extends JButton {
	private BufferedImage colorwell;
	private JDialog dialog = null;
	private JColorChooser chooser = null;
	private Color initialColor = null;

	/**
	 * Creates a new instance of JXColorSelectionButton
	 */
	public JXColorSelectionButton() {
		this(Color.red);
	}

	/**
	 * Creates a new instance of JXColorSelectionButton set to the specified
	 * color.
	 * 
	 * @param col
	 *            The default color
	 */
	public JXColorSelectionButton(Color col) {
		setBackground(col);
		this.addActionListener(new ActionHandler());
		this.setContentAreaFilled(false);
		this.setOpaque(false);

		try {
			colorwell = ImageIO.read(JXColorSelectionButton.class.getResourceAsStream("color/colorwell.png"));
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		this.addPropertyChangeListener("background", new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
				getChooser().setColor(getBackground());
			}
		});
	}

	/**
	 * A listener class to update the button's background when the selected
	 * color changes.
	 */
	private class ColorChangeListener implements ChangeListener {
		public JXColorSelectionButton button;

		public ColorChangeListener(JXColorSelectionButton button) {
			this.button = button;
		}

		public void stateChanged(ChangeEvent changeEvent) {
			button.setBackground(button.getChooser().getColor());
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void paintComponent(Graphics g) {
		// want disabledForeground when disabled, current colour otherwise
		final Color FILL_COLOR = isEnabled() ? PaintUtils.removeAlpha(getBackground()) : UIManagerExt.getSafeColor(
				"Button.disabledForeground", Color.LIGHT_GRAY);

		// draw the colorwell image (should only be on OSX)
		if (OS.isMacOSX() && colorwell != null) {
			Insets ins = new Insets(5, 5, 5, 5);
			GraphicsUtilities.tileStretchPaint(g, this, colorwell, ins);

			// fill in the color area
			g.setColor(FILL_COLOR);
			g.fillRect(ins.left, ins.top, getWidth() - ins.left - ins.right, getHeight() - ins.top - ins.bottom);
			// draw the borders
			g.setColor(PaintUtils.setBrightness(FILL_COLOR, 0.85f));
			g.drawRect(ins.left, ins.top, getWidth() - ins.left - ins.right - 1, getHeight() - ins.top - ins.bottom - 1);
			g.drawRect(ins.left + 1, ins.top + 1, getWidth() - ins.left - ins.right - 3, getHeight() - ins.top - ins.bottom - 3);
		} else {
			Graphics2D g2 = (Graphics2D) g.create();

			try {
				g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
				g2.setColor(Color.LIGHT_GRAY);
				final int DIAM = Math.min(getWidth(), getHeight());
				final int inset = 3;
				g2.fill(new Ellipse2D.Float(inset, inset, DIAM - 2 * inset, DIAM - 2 * inset));
				g2.setColor(FILL_COLOR);
				final int border = 1;
				g2.fill(new Ellipse2D.Float(inset + border, inset + border, DIAM - 2 * inset - 2 * border, DIAM - 2 * inset - 2
						* border));
			} finally {
				g2.dispose();
			}

		}
	}

	// /**
	// * Sample usage of JXColorSelectionButton
	// * @param args not used
	// */
	// public static void main(String[] args) {
	// javax.swing.JFrame frame = new javax.swing.JFrame("Color Button Test");
	// frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
	// javax.swing.JPanel panel = new javax.swing.JPanel();
	// javax.swing.JComponent btn = new JXColorSelectionButton();
	// btn.setEnabled(true);
	// panel.add(btn);
	// panel.add(new javax.swing.JLabel("ColorSelectionButton test"));
	//
	// frame.add(panel);
	// frame.pack();
	// frame.setVisible(true);
	// }

	/**
	 * Conditionally create and show the color chooser dialog.
	 */
	private void showDialog() {
		if (dialog == null) {
			dialog = JColorChooser.createDialog(JXColorSelectionButton.this, "Choose a color", true, getChooser(),
					new ActionListener() {
						public void actionPerformed(ActionEvent actionEvent) {
							Color color = getChooser().getColor();
							if (color != null) {
								setBackground(color);
							}
						}
					}, new ActionListener() {
						public void actionPerformed(ActionEvent actionEvent) {
							setBackground(initialColor);
						}
					});
			dialog.getContentPane().add(getChooser());
			getChooser().getSelectionModel().addChangeListener(new ColorChangeListener(JXColorSelectionButton.this));
		}

		initialColor = getBackground();
		dialog.setVisible(true);

	}

	/**
	 * Get the JColorChooser that is used by this JXColorSelectionButton. This
	 * chooser instance is shared between all invocations of the chooser, but is
	 * unique to this instance of JXColorSelectionButton.
	 * 
	 * @return the JColorChooser used by this JXColorSelectionButton
	 */
	public JColorChooser getChooser() {
		if (chooser == null) {
			chooser = new JColorChooser();
			// add the eyedropper color chooser panel
			chooser.addChooserPanel(new EyeDropperColorChooserPanel());
		}
		return chooser;
	}

	/**
	 * Set the JColorChooser that is used by this JXColorSelectionButton.
	 * chooser instance is shared between all invocations of the chooser, but is
	 * unique to this instance of JXColorSelectionButton.
	 * 
	 * @param chooser
	 *            The new JColorChooser to use.
	 */
	public void setChooser(JColorChooser chooser) {
		JColorChooser oldChooser = getChooser();
		this.chooser = chooser;
		firePropertyChange("chooser", oldChooser, chooser);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Dimension getPreferredSize() {
		if (isPreferredSizeSet() || colorwell == null) {
			return super.getPreferredSize();
		}

		return new Dimension(colorwell.getWidth(), colorwell.getHeight());
	}

	/**
	 * A private class to conditionally create and show the color chooser
	 * dialog.
	 */
	private class ActionHandler implements ActionListener {

		public void actionPerformed(ActionEvent actionEvent) {
			showDialog();
		}
	}
}
